package cn.zj.logistics.shiro;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import cn.zj.logistics.pojo.Permission;
import cn.zj.logistics.pojo.User;
import cn.zj.logistics.pojo.UserExample;
import cn.zj.logistics.pojo.UserExample.Criteria;
import cn.zj.logistics.service.PermissionService;
import cn.zj.logistics.service.UserService;

public class CustomRealm  extends AuthorizingRealm{
	
	@Autowired
	private UserService userService;
	
	
	@Autowired
	private PermissionService permissionService;
	
	/*
	 * 认证方法，实际开发在此方法中完成认证逻辑
	 * 
	 * 认证思路
	 * 
	 * 1，获取token中身份（账号）信息
	 * 
	 * 2，根据身份去数据库中查询对应的是否有此账号
	 * 2.1 注入Service层，Service调用dao层
	 * @Autowired
	 * private UserService userService;
	 * 2.2 调用根据账号查询用户方法
	 * User user = userService.selectUserByUsername(username);
	 * 
	 * 两种情况
	 * 2.2.1 user==null
	 * 当前认证方法直接返回null
	 * 	Shiro底层会抛 ： org.apache.shiro.authc.UnknownAccountException 账号不存在
	 * 
	 * 2.2.2 user!=null
	 * 	获取token用户输入的密码和user查询的密码比对（此操作Shiro完成，需进行第三步的配置）
	 * 
	 * 3，创建一个认证信息对象（AuthenticationInfo），将查询user对象作为参数传入进去，并返回
	 * 
	 * 4，Shiro底层完成密码的匹配操作（包括密码的加密算法匹配）
	 * 
	 * 
	 */
	
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		
		String username = (String) token.getPrincipal();
		
		UserExample example = new UserExample();
		Criteria criteria = example.createCriteria();
		criteria.andUsernameEqualTo(username);
		List<User> users = userService.selectByExample(example );
		
		if(users.size() == 0) {
			return null;
		}
		//获取用户
		User user = users.get(0);
		
		//获取凭证(密码)
		String hashedCredentials = user.getPassword();
		
		//获取盐（salt）
		String salt = user.getSalt();
		

		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, hashedCredentials, ByteSource.Util.bytes(salt), this.getName());
		
		return authenticationInfo;
	}
	

	
	/*
	 * 授权方法，实际开发在此方法中完成授权的逻辑
	 * 
	 * 授权思路
	 * 
	 * 1，获取当前身份
	 * User user = principals.getPrimaryPrincipal();
	 * 2，通过用户的角色id查询出对应的角色下面对应的所有的权限对应的权限表达式（list集合）
	 *     如：最终此身份拥有的权限
	 *     user:list,user:insert,user:delete,user:update,role:list
	 *     
	 * 3,创建授权信息对象（AuthorizationInfo）
	 * 	
	 * 4，将第二步查询出来的身份（当前用户）对应的所有的权限添加到 Shiro框架第三步创建的授权信息对象中
	 * 
	 * 
	 * 5,Shiro框架通过用户业务罗判断地方获取具体某一个权限表达式如：user:list和第四步添加进去的所有的权限表达中
	 * 	去匹配寻找是否有相同的
	 *  Shiro框架自己完成
	 * 
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取Shiro的主身份
		User user = (User) principals.getPrimaryPrincipal();	
		
		//获取身份的权限并且切割成字数组
		String[] permissionIds = user.getPermissionIds().split(",");
		//将字符串数组转换成long类型的集合
		List<Long> permissionIdsList = new ArrayList<>();
		for (String permissionId : permissionIds) {
			permissionIdsList.add(Long.valueOf(permissionId));
		}
		
		//根据权限id查询出对应的权限表达式集合
		List<String> expressions = permissionService.selectExpressionByPermisssionIds(permissionIdsList);
		System.out.println(expressions);
		
		//创建授权信息对象
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		
		//将权限表达添加到授权信息对象
		authorizationInfo.addStringPermissions(expressions);
		
		//把当前身份的角色名称放入Shiro框架中
		authorizationInfo.addRole(user.getRolename());
		
		
		return authorizationInfo;
	}

	

}
